import { ActionType } from '@/components/List'
import { differenceBy, findIndex, findLastIndex, get, uniq } from 'lodash-es'

type DotSplit<S extends string> = S extends `${infer A}.${infer B}`
	? B extends `${infer C}.${infer D}`
		? { [K in A]: DotSplit<`${C}.${D}`> }
		: { [K in A]: DotSplit<B> }
	: { [K in S]: string }

export type DropModel<K extends string> = DotSplit<K>

export function dealDrop<T extends DropModel<K>, K extends string = 'mtKey'>(
	prevList: T[],
	{ mts, dropIndex, isOther }: { isOther: boolean; dropIndex: number; mts: T[] },
	path = 'mtKey' as K
): T[] {
	if (isOther) {
		const insertList = differenceBy(mts, prevList, (x) => get(x, path))
		return [...prevList.slice(0, dropIndex), ...insertList, ...prevList.slice(dropIndex)]
	} else {
		const baseList = differenceBy(prevList, mts, (x) => get(x, path))
		let insertToBaseIndex = dropIndex
		const baseListMtKeys = baseList.map((l) => get(l, path))
		const nextFirstBaseListIndex = findIndex(prevList, (l) => baseListMtKeys.includes(get(l, path)), dropIndex)
		if (nextFirstBaseListIndex === -1) {
			insertToBaseIndex = baseList.length
		} else {
			insertToBaseIndex = baseList.findIndex((l) => get(l, path) === get(prevList[nextFirstBaseListIndex], path))
		}
		return [...baseList.slice(0, insertToBaseIndex), ...mts, ...baseList.slice(insertToBaseIndex)]
	}
}

export type OrderModel = {
	status: {
		checked: boolean
	}
}

export function listOrderClick<T extends OrderModel>(prevList: T[], type: ActionType) {
	const checkedList = prevList.filter((l) => l.status.checked)
	if (checkedList.length === 0) return prevList
	const unCheckedList = prevList.filter((l) => !l.status.checked)
	let insertToBaseIndex = 0
	switch (type) {
		case 'top':
			insertToBaseIndex = 0
			break
		case 'bottom':
			insertToBaseIndex = unCheckedList.length
			break
		case 'up':
			const firstCheckedTargetIndex = prevList.findIndex((l) => l.status.checked)
			if (firstCheckedTargetIndex === 0) {
				insertToBaseIndex = 0
			} else {
				insertToBaseIndex = firstCheckedTargetIndex - 1
			}
			break
		case 'down':
			const lastCheckedTargetIndex = findLastIndex(prevList, (l) => l.status.checked)
			if (lastCheckedTargetIndex === prevList.length - 1) {
				insertToBaseIndex = unCheckedList.length
			} else {
				insertToBaseIndex = lastCheckedTargetIndex + 2 - checkedList.length
			}
			break
		default:
			break
	}
	return [...unCheckedList.slice(0, insertToBaseIndex), ...checkedList, ...unCheckedList.slice(insertToBaseIndex)]
}

export type ToggleModel<K extends string> = DotSplit<K> & {
	status: {
		checked: boolean
	}
}

export type ReturnTypeListToggle<T> = { _start: string | null; _changes: string[]; list: T[] }

export type IAction = 'click' | 'ctrl' | 'shift'

export function listToggle<T extends ToggleModel<K>, K extends string = 'mtKey'>(
	prevList: T[],
	{
		_start,
		_changes,
		action,
		path = 'mtKey' as K,
	}: { _start: string | null; _changes: string[]; action: IAction; path?: K },
	uuid: string
): ReturnTypeListToggle<T> {
	if (action === 'click') {
		prevList.forEach((item) => {
			if (get(item, path) === uuid) {
				item.status.checked = true
			} else {
				item.status.checked = false
			}
		})
		return { list: [...prevList], _start: uuid!, _changes: [] }
	} else if (action === 'ctrl') {
		prevList.forEach((item) => {
			if (get(item, path) === uuid) {
				item.status.checked = !item.status.checked
			}
		})
		return { list: [...prevList], _start: uuid!, _changes: [] }
	} else {
		//保证 shift后的 changes 和 start 的选择状态一致
		//changes.length > 0 ：说明已经有过一次shift操作，需要把这次操作结果复原
		if (_changes.length > 0) {
			for (const item of prevList) {
				if (_changes.includes(get(item, path))) {
					item.status.checked = false
				}
			}
		}
		let newChanges = []
		let startItem = prevList.find((x) => get(x, path) === _start)
		if (startItem) {
			let _flag = false,
				_time = 0
			for (const item of prevList) {
				if (get(item, path) === uuid || get(item, path) === _start) {
					item.status.checked = true
					newChanges.push(get(item, path))
					_flag = ++_time === 1
				}
				if (_flag) {
					item.status.checked = true
					newChanges.push(get(item, path))
				}
			}
		}
		return { list: [...prevList], _start: _start, _changes: uniq(newChanges) }
	}
}
